#include <common/HeaderSpace.h>

#include <bits/stdc++.h>
#include <boost/algorithm/string.hpp>
#include <fcntl.h>
#include <folly/IPAddress.h>
#include <sys/stat.h>

namespace flashpp {

#ifdef USE_BDD
// std::mutex HeaderSpace::locker;
HeaderSpace
HeaderSpace::fromInt(uint32_t&& addr, int& prefixLength) {
  bdd result = bdd_true();
  std::bitset<32> bits(addr);

  locker.lock();
  for (int i = 31; i > (31 - prefixLength); i--) {
    // locker.lock();
    if (bits.test(i)) {
      result = result & bdd_ithvar(31 - i);
    } else {
      result = result & (bdd_nithvar(31 - i));
    }
    // locker.unlock();
  }
  locker.unlock();

  return HeaderSpace(std::move(result));
}

HeaderSpace
HeaderSpace::fromString(std::string addr) {
  std::vector<std::string> tokens;
  boost::split(tokens, addr, boost::is_any_of("/"));
  std::string ip = tokens[0];
  int prefixLen = std::atoi(tokens[1].c_str());

  auto maybeIp = folly::IPAddress::tryFromString(ip);
  if (maybeIp.hasError()) {
    throw folly::IPAddressFormatException(("Invalid IPv4 address '" + addr));
  }

  return fromInt(maybeIp.value().asV4().mask(prefixLen).toLongHBO(), prefixLen);
}

std::string
HeaderSpace::serialize() {
  std::string out;
  FILE* tmpFile = tmpfile();
  locker.lock();
  bdd_save(tmpFile, pred_);
  locker.unlock();

  rewind(tmpFile);
  struct stat sb {};
  fstat(tmpFile->_fileno, &sb);
  out.resize(sb.st_size);
  fread(const_cast<char*>(out.data()), sb.st_size, 1, tmpFile);
  fclose(tmpFile);
  return out;
}

HeaderSpace
HeaderSpace::deserialize(std::string data) {
  FILE* tmpFile = tmpfile();
  fwrite(data.data(), data.size(), 1, tmpFile);
  rewind(tmpFile);
  bdd pred;
  locker.lock();
  bdd_load(tmpFile, pred);
  locker.unlock();
  fclose(tmpFile);

  return HeaderSpace(pred);
}

HeaderSpace
HeaderSpace::operator&(const HeaderSpace& hs) const {
  locker.lock();
  auto r = HeaderSpace(std::move(pred_ & hs.pred()));
  locker.unlock();
  return r;
}

HeaderSpace
HeaderSpace::operator|(const HeaderSpace& hs) const {
  locker.lock();
  auto r = HeaderSpace(std::move(pred_ | hs.pred()));
  locker.unlock();
  return r;
}

HeaderSpace
HeaderSpace::operator!() const {
  locker.lock();
  auto r = HeaderSpace(std::move(!pred_));
  locker.unlock();
  return r;
}

bool
HeaderSpace::operator!=(const HeaderSpace& hs) const {
  return pred_ != hs.pred();
}

bool
HeaderSpace::operator==(const HeaderSpace& hs) const {
  return pred_ == hs.pred();
}
#endif

} // namespace flashpp

